# plyvel 无法在windows下使用,暂时弃用
import shutil
import time
import plyvel # type: ignore
import json


def obj_to_str(obj):
    """
    Recursively converts a Python object to a JSON-compatible string representation.
    Supports nested data structures including sets, lists, dictionaries, strings,
    integers, floats, booleans, None, and tuples.

    The conversion follows this format:
    {
        'type': <object_type>,
        'data': <converted_data>
    }
    where <object_type> can be 'set', 'list', 'dict', 'str', 'int', 'float', 'bool', 'None', or 'tuple'

    Args:
        obj: The Python object to be converted

    Returns:
        str: A JSON string representation of the object

    Examples:
        >>> obj_to_str({1, 2, 3})
        '{"type": "set", "data": [{"type": "int", "data": 1}, ...]}'
        >>> obj_to_str([1, "a", None])
        '{"type": "list", "data": [{"type": "int", "data": 1}, ...]}'
    """
    def _convert(obj):
        if obj is None:
            return {'type': 'None', 'data': None}
        elif type(obj) is bool:
            return {'type': 'bool', 'data': obj}
        elif type(obj) is int:
            return {'type': 'int', 'data': obj}
        elif type(obj) is float:
            return {'type': 'float', 'data': obj}
        elif type(obj) is str:
            return {'type': 'str', 'data': obj}
        elif type(obj) is list:
            return {'type': 'list', 'data': [_convert(item) for item in obj]}
        elif type(obj) is tuple:
            return {'type': 'tuple', 'data': [_convert(item) for item in obj]}
        elif type(obj) is dict:
            return {
                'type': 'dict',
                'data': {k: _convert(v) for k, v in obj.items()}
            }
        elif type(obj) is set:
            return {
                'type': 'set',
                'data': [_convert(item) for item in obj]
            }
        else:
            raise TypeError(f"Unsupported type: {type(obj)}")

    converted = _convert(obj)
    return json.dumps(converted)


def str_to_obj(json_str):
    """
    Recursively converts a JSON-formatted string with type information back to Python objects.
    This is the inverse function of obj_to_str, supporting all the same types.

    Args:
        json_str: JSON string produced by obj_to_str function

    Returns:
        object: The reconstructed Python object

    Raises:
        ValueError: If the JSON string is malformed
        TypeError: If encountering unsupported type in the JSON
    """

    def _convert(d):
        type_name = d['type']
        data = d['data']

        if type_name == 'None':
            return None
        elif type_name == 'bool':
            return data
        elif type_name in ('int', 'float'):
            return int(data) if type_name == 'int' else float(data)
        elif type_name == 'str':
            return data
        elif type_name in ('list', 'tuple', 'set'):
            return [_convert(item) for item in data] if type_name == 'list' else \
                tuple(_convert(item) for item in data) if type_name == 'tuple' else \
                set(_convert(item) for item in data)
        elif type_name == 'dict':
            return {k: _convert(v) for k, v in data.items()}
        else:
            raise TypeError(f"Unsupported type in JSON: {type_name}")

    return _convert(json.loads(json_str))


class LevelDBDict:
    def __init__(self, path, create_if_missing=True, encoding='utf-8', more_type_suspect=False):
        """
        初始化LevelDB数据库实例
        :param path: 数据库存储路径
        :param create_if_missing: 若路径不存在是否创建新数据库
        :param encoding: 字符编码(默认utf-8)
        """
        self.db = plyvel.DB(path, create_if_missing=create_if_missing)
        self.encoding = encoding
        self.more_type_suspect = more_type_suspect
        self.path = path
        self._count = -2
        # 记录开始时间
        start_time = time.time()
        # 先遍历,如果耗时大于1秒则直接初始化,否则先不初始化
        for _ in self.db.iterator():
            self._count += 1
            # 比较耗时
            if time.time() - start_time > 0.1:
                self._count = -1
                break
        # 如果没有数据,则初始化
        if self._count == -2:
            self._count = 0

    def __getitem__(self, key):
        if not isinstance(key, str):
            raise TypeError("键必须是字符串类型")

        value = self.db.get(key.encode(self.encoding))
        if value is None:
            raise KeyError(key)
        if self.more_type_suspect:
            return str_to_obj(value.decode(self.encoding))
        return value.decode(self.encoding)

    def __setitem__(self, key, value):
        if not isinstance(key, str):
            raise TypeError("键必须是字符串类型")
        if not self.more_type_suspect and not isinstance(value, str):
            raise TypeError("键和值必须是字符串类型")
        # 如果self._count>-1,则说明初始化过长度,则更新长度
        if self._count > -1 and key not in self:
            # 判断是否是新增
            self._count += 1
        if self.more_type_suspect:
            value = obj_to_str(value)
        self.db.put(key.encode(self.encoding), value.encode(self.encoding))

    def __delitem__(self, key):
        if not isinstance(key, str):
            raise TypeError("键必须是字符串类型")
        self.db.delete(key.encode(self.encoding))

    def __contains__(self, key):
        if not isinstance(key, str):
            return False
        return self.db.get(key.encode(self.encoding)) is not None

    def __iter__(self):
        for key in self.keys():
            yield key

    def keys(self):
        """ 返回键的生成器 """
        for key in self.db.iterator(include_value=False):
            yield key.decode(self.encoding)

    def values(self):
        """ 返回值的生成器 """
        for value in self.db.iterator(include_key=False):
            if self.more_type_suspect:
                yield str_to_obj(value.decode(self.encoding))
            else:
                yield value.decode(self.encoding)

    def items(self):
        """ 返回键值对的生成器 """
        for key, value in self.db.iterator():
            if self.more_type_suspect:
                value = str_to_obj(value.decode(self.encoding))
            else:
                value = value.decode(self.encoding)

            yield (key.decode(self.encoding), value)

    def close(self):
        self.db.close()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    def clear(self):
        """
        通过删除数据库文件实现极速清空

        :操作流程:
        1. 关闭数据库连接
        2. 删除数据库目录及其所有文件
        3. 重新创建空数据库

        :注意:
        - 操作不可逆，数据会永久丢失
        - 会短暂释放数据库文件句柄
        """
        if not hasattr(self, 'db'):
            return  # 未初始化时直接返回

        # 关闭现有数据库连接
        self.db.close()
        del self.db

        # 删除数据库目录
        shutil.rmtree(self.path, ignore_errors=True)

        # 重新创建数据库
        self.db = plyvel.DB(self.path, create_if_missing=True)

        # 重置计数
        self._count = 0

    def get(self, __key, __default = None):
        try:
            return self.__getitem__(__key)
        except KeyError:
            return __default

    def pop(self, __key, __default = None):
        result = self.get(__key, __default)
        try:
            self.__delitem__(__key)
        except KeyError:
            pass
        return result

    def __len__(self) -> int:
        """
        获取数据库中的键值对数量（智能选择最优计数方式）

        :性能策略:
        - 首次调用：完整遍历并缓存结果
        - 后续调用：返回缓存值（需要维护计数一致性）
        - 调用clear()后自动重置缓存

        :注意: 在并发写入场景需要外部加锁保证一致性
        """
        if self._count <= -1:
            self._count = sum(1 for _ in self.db)
        return self._count
